import { isString, isNumber, isNumeric } from '../common'
import XEUtils from 'xe-utils'

//   加      减        乘       除
// plus,   minus,  multiply,  divide
import Big from 'big.js'
/**
 * The maximum number of decimal places and the rounding mode used to round
 * the results of these methods is determined by the value of the DP and RM properties of the Big number constructor.
 * 用于对这些方法的结果进行舍入的最大小数位数和舍入模式由大数构造函数的DP和RM属性值决定。
 */
Big.DP = 2 // 浮点位数(精度位数)
/*
 * The rounding mode (RM) used when rounding to the above decimal places.
 *
 *  0  Towards zero (i.e. truncate, no rounding).       (ROUND_DOWN)
 *  1  To nearest neighbour. If equidistant, round up.  (ROUND_HALF_UP)
 *  2  To nearest neighbour. If equidistant, to even.   (ROUND_HALF_EVEN)
 *  3  Away from zero.                                  (ROUND_UP)
 */
Big.RM = 1 // 四舍五入的模式

export const plus = (a, b, dp = 2) => {
  Big.DP = dp
  a = a || 0
  b = b || 0
  return new Big(a).plus(b).toFixed(dp)
}
export const minus = (a, b, dp = 2) => {
  Big.DP = dp
  a = a || 0
  b = b || 0
  return new Big(a).minus(b).toFixed(dp)
}
export const multiply = (a, b, dp = 2) => {
  Big.DP = dp
  a = a || 0
  b = b || 0
  return new Big(a).times(b).toFixed(dp)
}
export const divide = (a, b, dp = 2) => {
  a = a || 0
  b = b || 1
  Big.DP = dp
  const result = new Big(a).div(b).toFixed(dp)
  return result
}

// test
// const math1 = Math.random();
// const math2 = Math.random();
// const a1 = divide(math1, math2, 1);
// const a2 = divide(math1, math2, 2);
// const a3 = divide(math1, math2, 3);
// console.log(a1, a2, a3);
// const b1 = plus(math1, math2, 1);
// const b2 = plus(math1, math2);
// const b3 = plus(math1, math2, 3);
// console.log(b1, b2, b3);
// const c1 = minus(math1, math2, 1);
// const c2 = minus(math1, math2);
// const c3 = minus(math1, math2, 3);
// console.log(c1, c2, c3);
// const d1 = multiply(math1, math2, 1);
// const d2 = multiply(math1, math2);
// const d3 = multiply(math1, math2, 3);
// console.log(d1, d2, d3);

// 去零函数 避免小数点过大导致科学计数法E
export function handleCutZero(num) {
  // 拷贝一份 返回去掉零的新串
  let newstr = num
  // 循环变量 小数部分长度
  // console.log(num.indexOf('.') - 1);
  const leng = num.length - num.indexOf('.') - 1
  // 判断是否有效数
  if (num.indexOf('.') > -1) {
    // 循环小数部分
    for (let i = leng; i > 0; i--) {
      // 如果newstr末尾有0
      if (newstr.lastIndexOf('0') > -1 && newstr.substr(newstr.length - 1, 1) == 0) {
        const k = newstr.lastIndexOf('0')
        // 如果小数点后只有一个0 去掉小数点
        if (newstr.charAt(k - 1) == '.') {
          return newstr.substring(0, k - 1)
        } else {
          // 否则 去掉一个0
          newstr = newstr.substring(0, k)
        }
      } else {
        // 如果末尾没有0
        return newstr
      }
    }
  }
  return num
}

// 截取小数位数
export function truncateDecimal<T extends number | string | any>(
  value: T,
  decimalPlaces: number | string = 0
): T | string {
  if (!(isString(value) || isNumber(value))) return value
  let str = value.toString()
  const strIndex = str.indexOf('.')
  if (strIndex === -1) return value
  if (decimalPlaces === 0 || decimalPlaces === '0') {
    str = str.substring(0, strIndex)
  } else {
    str = str.substring(0, strIndex + 1 + Number(decimalPlaces))
  }
  return str
}

/** 保留小数 */
export const formatNumber = (
  num: number | string | any,
  decimalPlaces: number = 2,
  options: { retainZero?: boolean; returnType?: 'number' | 'string' } = {
    retainZero: false,
    returnType: 'number'
  }
) => {
  const roundFn = options.retainZero ? XEUtils.toFixed : XEUtils.round
  const transformFn = options.returnType === 'number' ? XEUtils.toNumber : XEUtils.toNumberString
  return transformFn(roundFn(num, decimalPlaces))
}

/**
 * 保留小数 支持对象，仅对数字类型进行格式化
 */
export const formatNumbersInObject = <D extends object>(
  target: D,
  rules: Record<
    string,
    number | { decimalPlaces: number; retainZero?: boolean; returnType?: 'number' | 'string' }
  > = {},
  defaultRule:
    | number
    | { decimalPlaces: number; retainZero?: boolean; returnType?: 'number' | 'string' } = 2
): D => {
  // 获取格式化规则配置
  const getRuleConfig = (key: string) => {
    let config = {
      decimalPlaces: 2,
      retainZero: false,
      returnType: 'number' as 'number' | 'string'
    }

    // 优先使用字段专属规则
    const rule = rules[key] || defaultRule

    if (isNumber(rule)) {
      config.decimalPlaces = rule
    } else {
      config = { ...config, ...rule }
    }

    return config
  }

  const formatted = { ...target }

  Object.entries(formatted).forEach(([key, value]) => {
    if (isNumeric(value)) {
      const { decimalPlaces, retainZero, returnType } = getRuleConfig(key)
      formatted[key] = formatNumber(value, decimalPlaces, { retainZero, returnType })
    }
  })

  return formatted
}
